//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package watch_dao

import (
	"context"
	"fmt"

	"atomgit.com/openlinksaas-org/api-server/dao/db_ns"
	"atomgit.com/openlinksaas-org/proto-gen-go.git/project_watch_api"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type MyWatchInfoDbItem struct {
	MyWatchId  string `bson:"_id"`
	ProjectId  string `bson:"projectId"`
	UserId     string `bson:"userId"`
	TargetId   string `bson:"targetId"`
	TargetType uint32 `bson:"targetType"`
	WatchTime  int64  `bson:"watchTime"`
}

func (item *MyWatchInfoDbItem) ToMyWatchInfo() *project_watch_api.MyWatchInfo {
	return &project_watch_api.MyWatchInfo{
		TargetId:   item.TargetId,
		TargetType: project_watch_api.WATCH_TARGET_TYPE(item.TargetType),
		Title:      "",
		WatchTime:  item.WatchTime,
	}
}

type _MyWatchDao struct {
	mc *mongo.Collection
}

func newMyWatchDao(db *mongo.Database) (*_MyWatchDao, error) {
	mc := db.Collection(db_ns.DB_NS_WATCH + "my_watch")
	_, err := mc.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
		{
			Keys: bson.D{{"projectId", 1}, {"userId", 1}, {"targetType", 1}, {"watchTime", -1}},
		},
		{
			Keys: bson.D{{"projectId", 1}, {"targetType", 1}, {"targetId", -1}},
		},
	})
	if err != nil {
		return nil, err
	}
	return &_MyWatchDao{mc: mc}, nil
}

func (db *_MyWatchDao) Insert(ctx context.Context, dbItem *MyWatchInfoDbItem) error {
	dbItem.MyWatchId = db.genMyWatchId(dbItem.ProjectId, dbItem.UserId, dbItem.TargetId)
	_, err := db.mc.InsertOne(ctx, dbItem)
	return err
}

func (db *_MyWatchDao) genMyWatchId(projectId, userId, targetId string) string {
	return fmt.Sprintf("%s:%s:%s", projectId, userId, targetId)
}

func (db *_MyWatchDao) Exist(ctx context.Context, projectId, userId, targetId string, targetType uint32) (bool, error) {
	myWatchId := db.genMyWatchId(projectId, userId, targetId)
	count, err := db.mc.CountDocuments(ctx, bson.M{"_id": myWatchId, "targetType": targetType})
	return count > 0, err
}

func (db *_MyWatchDao) Get(ctx context.Context, projectId, userId, targetId string, targetType uint32) (*MyWatchInfoDbItem, error) {
	myWatchId := db.genMyWatchId(projectId, userId, targetId)
	retItem := &MyWatchInfoDbItem{}
	err := db.mc.FindOne(ctx, bson.M{"_id": myWatchId, "targetType": targetType}).Decode(retItem)
	return retItem, err
}

func (db *_MyWatchDao) Remove(ctx context.Context, projectId, userId, targetId string, targetType uint32) error {
	myWatchId := db.genMyWatchId(projectId, userId, targetId)
	_, err := db.mc.DeleteOne(ctx, bson.M{"_id": myWatchId, "targetType": targetType})
	return err
}

func (db *_MyWatchDao) RemoveByTarget(ctx context.Context, projectId string, targetType uint32, targetId string) error {
	_, err := db.mc.DeleteMany(ctx, bson.M{"projectId": projectId, "targetType": targetType, "targetId": targetId})
	return err
}

func (db *_MyWatchDao) RemoveByUser(ctx context.Context, projectId, userId string) error {
	_, err := db.mc.DeleteMany(ctx, bson.M{"projectId": projectId, "userId": userId})
	return err
}

// 移除cicd后，数据修复用
func (db *_MyWatchDao) RemoveCicdTarget(ctx context.Context) error {
	// WATCH_TARGET_CI_CD = 4;         // CI/CD(废弃)
	_, err := db.mc.DeleteMany(ctx, bson.M{"targetType": 4})
	return err
}

func (db *_MyWatchDao) Count(ctx context.Context, projectId, userId string, filterByType bool, targetType uint32) (uint32, error) {
	query := bson.M{
		"projectId": projectId,
		"userId":    userId,
	}
	if filterByType {
		query["targetType"] = targetType
	}
	count, err := db.mc.CountDocuments(ctx, query)
	return uint32(count), err
}

func (db *_MyWatchDao) List(ctx context.Context, projectId, userId string, filterByType bool, targetType uint32, offset, limit int64) ([]*MyWatchInfoDbItem, error) {
	query := bson.M{
		"projectId": projectId,
		"userId":    userId,
	}
	if filterByType {
		query["targetType"] = targetType
	}
	cursor, err := db.mc.Find(ctx, query, options.Find().SetSkip(offset).SetLimit(limit).SetSort(bson.M{"watchTime": -1}))
	if err != nil {
		return nil, err
	}
	retList := []*MyWatchInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_MyWatchDao) CalcWatchUserMap(ctx context.Context, projectId string, targetType uint32, targetIdList []string) (map[string][]string, []string, error) {
	query := bson.M{
		"projectId":  projectId,
		"targetType": targetType,
		"targetId":   bson.M{"$in": targetIdList},
	}
	cursor, err := db.mc.Find(ctx, query)
	if err != nil {
		return nil, nil, err
	}
	itemList := []*MyWatchInfoDbItem{}
	err = cursor.All(ctx, &itemList)
	if err != nil {
		return nil, nil, err
	}

	targetMap := map[string][]string{}
	allUserList := []string{}
	for _, item := range itemList {
		tmpList, ok := targetMap[item.TargetId]
		if !ok {
			tmpList = []string{}
		}
		tmpList = append(tmpList, item.UserId)
		targetMap[item.TargetId] = tmpList
		allUserList = append(allUserList, item.UserId)
	}

	return targetMap, allUserList, nil
}

func (db *_MyWatchDao) DistinctTargetId(ctx context.Context, projectId, userId string, targetType uint32) ([]string, error) {
	itemList, err := db.mc.Distinct(ctx, "targetId", bson.M{"projectId": projectId, "userId": userId, "targetType": targetType})
	if err != nil {
		return nil, err
	}
	retList := []string{}
	for _, item := range itemList {
		retList = append(retList, item.(string))
	}
	return retList, nil
}

func (db *_MyWatchDao) DistinctUserId(ctx context.Context, projectId string, targetType uint32, targetId string) ([]string, error) {
	itemList, err := db.mc.Distinct(ctx, "userId", bson.M{"projectId": projectId, "targetType": targetType, "targetId": targetId})
	if err != nil {
		return nil, err
	}
	retList := []string{}
	for _, item := range itemList {
		retList = append(retList, item.(string))
	}
	return retList, nil
}

// 把api coll和dta anno 类型设置为entry
// WATCH_TARGET_API_COLL = 5;      // API集合
// WATCH_TARGET_DATA_ANNO = 6;     // 数据标注
func (db *_MyWatchDao) Upgrade(ctx context.Context) error {
	_, err := db.mc.UpdateMany(ctx, bson.M{"targetType": bson.M{"$in": []uint32{5, 6}}}, bson.M{"$set": bson.M{"targetType": 0}})
	return err
}

func (db *_MyWatchDao) ListAllEntry(ctx context.Context) ([]*MyWatchInfoDbItem, error) {
	cursor, err := db.mc.Find(ctx, bson.M{"targetType": uint32(project_watch_api.WATCH_TARGET_TYPE_WATCH_TARGET_ENTRY)})
	if err != nil {
		return nil, err
	}
	retList := []*MyWatchInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}
